// Geometric Tools, Inc.
// http://www.geometrictools.com
// Copyright (c) 1998-2006.  All Rights Reserved
//
// The Wild Magic Library (WM3) source code is supplied under the terms of
// the license agreement
//     http://www.geometrictools.com/License/WildMagic3License.pdf
// and may not be copied or disclosed except in accordance with the terms
// of that agreement.

#include "Wm3FoundationPCH.h"
#include "Wm3CurveMesh.h"
using namespace Wm3;

WM3_IMPLEMENT_RTTI(Wm3,CurveMesh,Polyline);
WM3_IMPLEMENT_STREAM(CurveMesh);

//----------------------------------------------------------------------------
CurveMesh::CurveMesh (int iSegmentQuantity, CurveSegmentPtr* aspkSegment,
    FloatArrayPtr spkParams, bool bAllowAttributes, bool bAllowDynamicChange)
    :
    Polyline(WM3_NEW Vector3fArray(2,WM3_NEW Vector3f[2]),false,true)
{
    assert(iSegmentQuantity > 0 && aspkSegment);
    assert(spkParams && spkParams->GetData());
    assert(spkParams->GetQuantity() == 2*iSegmentQuantity);

    // zero-out the default polyline vertices
    Vector3f* akVertex = Vertices->GetData();
    memset(akVertex,0,2*sizeof(Vector3f));

    m_iSegmentQuantity = iSegmentQuantity;
    m_aspkSegment = aspkSegment;
    m_spkOrigParams = spkParams;
    m_iLevel = 0;
    m_iFullVQuantity = 0;  // determined by SetLevel
    m_bAllowAttributes = bAllowAttributes;
    m_akVTree = 0;
    m_bAllowDynamicChange = bAllowDynamicChange;
    m_akCInfo = 0;

    SetLevel(-1);
}
//----------------------------------------------------------------------------
CurveMesh::CurveMesh ()
{
    m_iSegmentQuantity = 0;
    m_aspkSegment = 0;
    m_iLevel = 0;
    m_iFullVQuantity = 0;
    m_bAllowAttributes = false;
    m_akVTree = 0;
    m_bAllowDynamicChange = false;
    m_akCInfo = 0;
}
//----------------------------------------------------------------------------
CurveMesh::~CurveMesh ()
{
    WM3_DELETE[] m_aspkSegment;
    WM3_DELETE[] m_akVTree;
    WM3_DELETE[] m_akCInfo;
}
//----------------------------------------------------------------------------
void CurveMesh::SetLevel (int iLevel)
{
    if (!m_spkOrigParams)
    {
        // the mesh is locked, no subdivision allowed
        return;
    }

    if (iLevel < 0)
    {
        m_iLevel = 0;
    }
    else if (iLevel != m_iLevel)
    {
        m_iLevel = iLevel;
    }
    else  // input level is current level, nothing to do
    {
        return;
    }

    // allocate the storage for subdivision
    int iVQTotal, iEQTotal;
    Vector3f* akVertex;
    Edge* akEdge;
    Allocate(iVQTotal,akVertex,iEQTotal,akEdge);

    // subdivide the mesh
    int iVQ = m_iSegmentQuantity + 1;
    int iEQ = m_iSegmentQuantity;
    int i;
    for (i = 0; i < m_iLevel; i++)
    {
        int iVQNext = iVQ + iEQ;
        int iEQNext = 2*iEQ;
        Subdivide(iVQ,akVertex,iEQ,akEdge);
        assert(iVQ == iVQNext && iEQ == iEQNext);
        iVQ = iVQNext;
        iEQ = iEQNext;
    }

    // check to make sure the implementation is correct
    assert(iVQ == iVQTotal && iEQ == iEQTotal);
    m_iActiveQuantity = m_iFullVQuantity = iVQTotal;

    // create the polyline for the subdivision
    Vertices = WM3_NEW Vector3fArray(iVQTotal,akVertex);
    UpdateModelBound();

    int* aiIndex = WM3_NEW int[iVQTotal];
    int* piIndex = aiIndex;
    for (i = 0; i < iEQTotal; i++)
    {
        *piIndex++ = akEdge[i].V[0];
    }
    *piIndex++ = akEdge[iEQTotal-1].V[1];
    Indices = WM3_NEW IntArray(iVQTotal,aiIndex);

    WM3_DELETE[] akEdge;

    OnDynamicChange();
}
//----------------------------------------------------------------------------
void CurveMesh::Allocate (int& riVQTotal, Vector3f*& rakVertex,
    int& riEQTotal, Edge*& rakEdge)
{
    // the number of original vertices
    int iVQOrig = m_iSegmentQuantity + 1;

    // the number of original edges
    int iEQOrig = m_iSegmentQuantity;

    // determine the various quantities needed for subdivision
    riVQTotal = iVQOrig;
    riEQTotal = iEQOrig;
    int i;
    for (i = 0; i < m_iLevel; i++)
    {
        riVQTotal = riVQTotal + riEQTotal;
        riEQTotal = 2*riEQTotal;
    }

    // allocate storage for the subdivision
    rakVertex = WM3_NEW Vector3f[riVQTotal];
    rakEdge = WM3_NEW Edge[riEQTotal];
    if (m_bAllowAttributes)
    {
        WM3_DELETE[] m_akVTree;
        m_akVTree = WM3_NEW VertexTree[riVQTotal];
    }
    if (m_bAllowDynamicChange)
    {
        WM3_DELETE[] m_akCInfo;
        m_akCInfo = WM3_NEW CurveInfo[riVQTotal];
        InitializeCurveInfo();
    }

    // compute the end point vertices
    const float* afParam = m_spkOrigParams->GetData();
    for (i = 0; i < m_iSegmentQuantity; i++)
    {
        rakVertex[i] = m_aspkSegment[i]->P(afParam[2*i]);
    }
    rakVertex[m_iSegmentQuantity] = m_aspkSegment[m_iSegmentQuantity-1]->P(
        afParam[2*m_iSegmentQuantity-1]);

    // initialize the edge data structures
    int iIndex = 0;
    for (i = 0; i < iEQOrig; i++)
    {
        Edge& rkE = rakEdge[i];

        // curve affecting the edge
        rkE.Segment = m_aspkSegment[i];

        // get the vertex indices for the edge
        rkE.V[0] = iIndex++;
        rkE.V[1] = iIndex;

        // get the parameters associated with the vertices
        rkE.Param[0] = *afParam++;
        rkE.Param[1] = *afParam++;
    }
}
//----------------------------------------------------------------------------
void CurveMesh::Subdivide (int& riVQ, Vector3f* akVertex, int& riEQ,
    Edge* akEdge)
{
    // The subdivision is computed in-place, so you need to fill in the new
    // edge slots starting from the end of the array and working towards the
    // beginning.  This avoid overwriting old edges not yet subdivided.
    for (int iOld = riEQ-1, iCurr = 2*riEQ-1; iOld >= 0; iOld--)
    {
        Edge& rkE = akEdge[iOld];

        // compute the vertex at the average of the parameter values
        float fParamMid = 0.5f*(rkE.Param[0] + rkE.Param[1]);
        akVertex[riVQ] = rkE.Segment->P(fParamMid);

        if (m_bAllowAttributes)
        {
            m_akVTree[riVQ].V[0] = rkE.V[0];
            m_akVTree[riVQ].V[1] = rkE.V[1];
        }

        if (m_bAllowDynamicChange)
        {
            m_akCInfo[riVQ].Segment = rkE.Segment;
            m_akCInfo[riVQ].Param = fParamMid;
        }

        // subdivide the edge
        Edge& rkE1 = akEdge[iCurr--];
        rkE1.Segment = rkE.Segment;
        rkE1.V[0] = riVQ;
        rkE1.V[1] = rkE.V[1];
        rkE1.Param[0] = fParamMid;
        rkE1.Param[1] = rkE.Param[1];

        Edge& rkE0 = akEdge[iCurr--];
        rkE0.Segment = rkE.Segment;
        rkE0.V[0] = rkE.V[0];
        rkE0.V[1] = riVQ;
        rkE0.Param[0] = rkE.Param[0];
        rkE0.Param[1] = fParamMid;

        riVQ++;
    }

    riEQ = 2*riEQ;
}
//----------------------------------------------------------------------------
float* CurveMesh::GetAttributes (int iSize, const float* afAttr) const
{
    if (!m_bAllowAttributes)
    {
        return 0;
    }

    int iOrigVQuantity = m_iSegmentQuantity + 1;
    int iFQuantity = iSize*m_iFullVQuantity;
    float* afFullAttr = WM3_NEW float[iFQuantity];

    // use a direct copy for the original vertex attributes
    size_t uiDstSize = iFQuantity*sizeof(float);
    size_t uiSrcSize = iSize*iOrigVQuantity*sizeof(float);
    System::Memcpy(afFullAttr,uiDstSize,afAttr,uiSrcSize);

    // Traverse the vertex tree to compute the remaining attributes.  The
    // order of vertex insertion guarantees that the child attributes are
    // already computed, so a recursive call is not necessary.
    for (int i = iOrigVQuantity; i < m_iFullVQuantity; i++)
    {
        VertexTree& rkNode = m_akVTree[i];
        assert(rkNode.V[0] >= 0);

        float* afValue = &afFullAttr[iSize*i];
        float* afCValue0 = &afFullAttr[iSize*rkNode.V[0]];
        float* afCValue1 = &afFullAttr[iSize*rkNode.V[1]];
        for (int j = 0; j < iSize; j++)
        {
            // compute the attribute to be the average of attributes
            afValue[j] = 0.5f*(afCValue0[j] + afCValue1[j]);
        }
    }

    return afFullAttr;
}
//----------------------------------------------------------------------------
void CurveMesh::OnDynamicChange ()
{
    if (m_bAllowDynamicChange)
    {
        Vector3f* akVertex = Vertices->GetData();
        for (int i = 0; i < m_iFullVQuantity; i++)
        {
            CurveInfo& rkCI = m_akCInfo[i];
            akVertex[i] = rkCI.Segment->P(rkCI.Param);
        }

        UpdateMS();
    }
}
//----------------------------------------------------------------------------
void CurveMesh::Lock ()
{
    if (m_spkOrigParams)
    {
        m_spkOrigParams = 0;
        WM3_DELETE[] m_aspkSegment;
        m_aspkSegment = 0;
        WM3_DELETE[] m_akVTree;
        m_akVTree = 0;
    }

    // The array m_akCInfo is not deleted.  This allows dynamic updates of
    // the vertices of the mesh, even though the mesh is "locked".
}
//----------------------------------------------------------------------------
void CurveMesh::InitializeCurveInfo ()
{
    // Because vertices are shared by edges, the last visited edge for a
    // vertex donates its segment and parameter values to that vertex.
    const float* afParam = m_spkOrigParams->GetData();
    int iIndex = 0;
    for (int i = 0; i < m_iSegmentQuantity; i++)
    {
        int iV0 = iIndex++;
        int iV1 = iIndex;
        m_akCInfo[iV0].Segment = m_aspkSegment[i];
        m_akCInfo[iV0].Param = *afParam++;
        m_akCInfo[iV1].Segment = m_aspkSegment[i];
        m_akCInfo[iV1].Param = *afParam++;
    }
}
//----------------------------------------------------------------------------
CurveMesh::Edge::Edge ()
{
    Segment = 0;
    V[0] = -1;
    V[1] = -1;
    Param[0] = 0.0f;
    Param[1] = 0.0f;
}
//----------------------------------------------------------------------------
CurveMesh::VertexTree::VertexTree ()
{
    V[0] = -1;
    V[1] = -1;
}
//----------------------------------------------------------------------------
CurveMesh::CurveInfo::CurveInfo ()
{
    Segment = 0;
    Param = 0.0f;
}
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// name and unique id
//----------------------------------------------------------------------------
Object* CurveMesh::GetObjectByName (const String& rkName)
{
    Object* pkFound = Polyline::GetObjectByName(rkName);
    if (pkFound)
    {
        return pkFound;
    }

    if (m_aspkSegment)
    {
        for (int i = 0; i < m_iSegmentQuantity; i++)
        {
            if (m_aspkSegment[i])
            {
                pkFound = m_aspkSegment[i]->GetObjectByName(rkName);
                if (pkFound)
                {
                    return pkFound;
                }
            }
        }
    }

    if (m_spkOrigParams)
    {
        pkFound = m_spkOrigParams->GetObjectByName(rkName);
        if (pkFound)
        {
            return pkFound;
        }
    }

    return 0;
}
//----------------------------------------------------------------------------
void CurveMesh::GetAllObjectsByName (const String& rkName,
    TArray<Object*>& rkObjects)
{
    Polyline::GetAllObjectsByName(rkName,rkObjects);

    if (m_aspkSegment)
    {
        for (int i = 0; i < m_iSegmentQuantity; i++)
        {
            if (m_aspkSegment[i])
            {
                m_aspkSegment[i]->GetAllObjectsByName(rkName,rkObjects);
            }
        }
    }

    if (m_spkOrigParams)
    {
        m_spkOrigParams->GetAllObjectsByName(rkName,rkObjects);
    }
}
//----------------------------------------------------------------------------
Object* CurveMesh::GetObjectByID (unsigned int uiID)
{
    Object* pkFound = Polyline::GetObjectByID(uiID);
    if (pkFound)
    {
        return pkFound;
    }

    if (m_aspkSegment)
    {
        for (int i = 0; i < m_iSegmentQuantity; i++)
        {
            if (m_aspkSegment[i])
            {
                pkFound = m_aspkSegment[i]->GetObjectByID(uiID);
                if (pkFound)
                {
                    return pkFound;
                }
            }
        }
    }

    if (m_spkOrigParams)
    {
        pkFound = m_spkOrigParams->GetObjectByID(uiID);
        if (pkFound)
        {
            return pkFound;
        }
    }

    return 0;
}
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// streaming
//----------------------------------------------------------------------------
void CurveMesh::Load (Stream& rkStream, Stream::Link* pkLink)
{
    WM3_BEGIN_DEBUG_STREAM_LOAD;

    Polyline::Load(rkStream,pkLink);

    // native data
    rkStream.Read(m_iSegmentQuantity);
    rkStream.Read(m_iLevel);
    rkStream.Read(m_iFullVQuantity);
    rkStream.Read(m_bAllowAttributes);
    rkStream.Read(m_bAllowDynamicChange);

    // link data
    Object* pkObject;
    rkStream.Read(pkObject);  // m_spkOrigParams
    pkLink->Add(pkObject);
    bool bLocked = (pkObject == 0);

    if (!bLocked)
    {
        m_aspkSegment = WM3_NEW CurveSegmentPtr[m_iSegmentQuantity];
        for (int i = 0; i < m_iSegmentQuantity; i++)
        {
            rkStream.Read(pkObject);  // m_aspkSegment[i]
            pkLink->Add(pkObject);
        }
    }

    if (m_bAllowAttributes && !bLocked)
    {
        m_akVTree = WM3_NEW VertexTree[m_iFullVQuantity];
        rkStream.Read(2*m_iFullVQuantity,(int*)m_akVTree);
    }

    if (m_bAllowDynamicChange)
    {
        m_akCInfo = WM3_NEW CurveInfo[m_iFullVQuantity];
        for (int i = 0; i < m_iFullVQuantity; i++)
        {
            rkStream.Read(pkObject);
            pkLink->Add(pkObject);
            rkStream.Read(m_akCInfo[i].Param);
        }
    }

    WM3_END_DEBUG_STREAM_LOAD(CurveMesh);
}
//----------------------------------------------------------------------------
void CurveMesh::Link (Stream& rkStream, Stream::Link* pkLink)
{
    Polyline::Link(rkStream,pkLink);

    Object* pkLinkID;
    if (m_aspkSegment)
    {
        for (int i = 0; i < m_iSegmentQuantity; i++)
        {
            pkLinkID = pkLink->GetLinkID();
            m_aspkSegment[i] = (CurveSegment*)rkStream.GetFromMap(pkLinkID);
        }
    }

    pkLinkID = pkLink->GetLinkID();
    m_spkOrigParams = (FloatArray*)rkStream.GetFromMap(pkLinkID);

    if (m_akCInfo)
    {
        for (int i = 0; i < m_iFullVQuantity; i++)
        {
            pkLinkID = pkLink->GetLinkID();
            m_akCInfo[i].Segment =
                (CurveSegment*)rkStream.GetFromMap(pkLinkID);
        }
    }
}
//----------------------------------------------------------------------------
bool CurveMesh::Register (Stream& rkStream) const
{
    if (!Polyline::Register(rkStream))
    {
        return false;
    }

    if (m_aspkSegment)
    {
        for (int i = 0; i < m_iSegmentQuantity; i++)
        {
            if (m_aspkSegment[i])
            {
                m_aspkSegment[i]->Register(rkStream);
            }
        }
    }

    if (m_spkOrigParams)
    {
        m_spkOrigParams->Register(rkStream);
    }

    if (m_akCInfo)
    {
        for (int i = 0; i < m_iFullVQuantity; i++)
        {
            if (m_akCInfo[i].Segment)
            {
                m_akCInfo[i].Segment->Register(rkStream);
            }
        }
    }

    return true;
}
//----------------------------------------------------------------------------
void CurveMesh::Save (Stream& rkStream) const
{
    WM3_BEGIN_DEBUG_STREAM_SAVE;

    Polyline::Save(rkStream);

    // native data
    rkStream.Write(m_iSegmentQuantity);
    rkStream.Write(m_iLevel);
    rkStream.Write(m_iFullVQuantity);
    rkStream.Write(m_bAllowAttributes);
    rkStream.Write(m_bAllowDynamicChange);

    // link data
    if (m_aspkSegment)
    {
        for (int i = 0; i < m_iSegmentQuantity; i++)
        {
            rkStream.Write(m_aspkSegment[i]);
        }
    }

    rkStream.Write(m_spkOrigParams);

    if (m_akVTree)
    {
        rkStream.Write(2*m_iFullVQuantity,(int*)m_akVTree);
    }

    if (m_akCInfo)
    {
        for (int i = 0; i < m_iFullVQuantity; i++)
        {
            rkStream.Write(m_akCInfo[i].Segment);
            rkStream.Write(m_akCInfo[i].Param);
        }
    }

    WM3_END_DEBUG_STREAM_SAVE(CurveMesh);
}
//----------------------------------------------------------------------------
StringTree* CurveMesh::SaveStrings (const char*)
{
    int i, iCQuantity = 1;

    if (m_aspkSegment)
    {
        for (i = 0; i < m_iSegmentQuantity; i++)
        {
            if (m_aspkSegment[i])
            {
                iCQuantity++;
            }
        }
    }
    if (m_spkOrigParams)
    {
        iCQuantity++;
    }

    StringTree* pkTree = WM3_NEW StringTree(3,iCQuantity);

    // strings
    pkTree->SetString(0,Format(&TYPE,GetName()));
    pkTree->SetString(1,Format("level =",m_iLevel));
    pkTree->SetString(1,Format("locked =",IsLocked()));

    // children
    pkTree->SetChild(0,Polyline::SaveStrings());
    iCQuantity = 1;
    if (m_aspkSegment)
    {
        for (i = 0; i < m_iSegmentQuantity; i++)
        {
            if (m_aspkSegment[i])
            {
                pkTree->SetChild(iCQuantity++,
                    m_aspkSegment[i]->SaveStrings());
            }
        }
    }
    if (m_spkOrigParams)
    {
        pkTree->SetChild(iCQuantity++,
            m_spkOrigParams->SaveStrings("original parameters"));
    }

    return pkTree;
}
//----------------------------------------------------------------------------
int CurveMesh::GetMemoryUsed () const
{
    int iSize = sizeof(CurveMesh) - sizeof(Polyline) +
        Polyline::GetMemoryUsed();

    if (m_aspkSegment)
    {
        iSize += m_iSegmentQuantity*sizeof(m_aspkSegment[0]);
    }

    if (m_akVTree)
    {
        iSize += m_iFullVQuantity*sizeof(m_akVTree[0]);
    }

    if (m_akCInfo)
    {
        iSize += m_iFullVQuantity*sizeof(m_akCInfo[0]);
    }

    return iSize;
}
//----------------------------------------------------------------------------
int CurveMesh::GetDiskUsed (const StreamVersion& rkVersion) const
{
    int iSize = Polyline::GetDiskUsed(rkVersion) +
        sizeof(m_iSegmentQuantity) +
        sizeof(m_iLevel) +
        sizeof(m_iFullVQuantity) +
        sizeof(char) +  // m_bAllowAttributes
        sizeof(char) +  // m_bAllowDynamicChange
        sizeof(m_spkOrigParams);

    if (m_aspkSegment)
    {
        iSize += m_iSegmentQuantity*sizeof(m_aspkSegment[0]);
    }

    if (m_akVTree)
    {
        iSize += m_iFullVQuantity*sizeof(m_akVTree[0]);
    }

    if (m_akCInfo)
    {
        iSize += m_iFullVQuantity*sizeof(m_akCInfo[0]);
    }

    return iSize;
}
//----------------------------------------------------------------------------

